﻿///////////////////////////////////////////////////////////////////////////////
//	Copyright 2013 JASDev International
//
//	Licensed under the Apache License, Version 2.0 (the "License");
//	you may not use this file except in compliance with the License.
//	You may obtain a copy of the License at
//
//		http://www.apache.org/licenses/LICENSE-2.0
//
//	Unless required by applicable law or agreed to in writing, software
//	distributed under the License is distributed on an "AS IS" BASIS,
//	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//	See the License for the specific language governing permissions and
//	limitations under the License.
///////////////////////////////////////////////////////////////////////////////

using System;
using System.Windows.Forms;

using JDI.Common.Logger;
using JDI.WebSocket.Client;

namespace JDI.WebSocket.WinFormApp
{
	public partial class MainForm : Form, ILogger
	{
		private FileLogger fileLogger;
		private const string logFilePath = "c:\\jdiWebSocket\\";
		private const string logFileName = "WinFormApp";
		private const string serverUrl = "wss://echo.websocket.org";
	//	private const string serverUrl = "ws://192.168.1.6/MvcApplication1/dev/echotest/jim";
	//	private const string serverUrl = "ws://192.168.1.6/MvcApplication1/dev/closetest/jim?secs=10";
	//	private const string serverUrl = "ws://192.168.1.6/MvcApplication1/dev/chattest/jim";
		private WebSocketClient webSocketClient;
		private Timer timer = null;

		public MainForm()
		{
			InitializeComponent();
		}


		#region Protected and Private Methods

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);

			// init logger
		//	this.fileLogger = new FileLogger();
		//	this.fileLogger.Open(logFilePath, logFileName);
		//	Logger.Initialize(this.fileLogger, LogLevel.Debug);
		//	Logger.Initialize(new DebugLogger(), LogLevel.Debug);
			Logger.Initialize(this, LogLevel.Info);

			// init network
			if (this.InitializeNetwork(true) == false)
			{
				// failed to initialize network
				return;
			}

			WSOptions wsOptions = new WSOptions();
			wsOptions.MaskingEnabled = true;
			wsOptions.ActivityTimerEnabled = true;

			// init websocket client
			this.webSocketClient = new WebSocketClient("websocket", wsOptions);

			// attach event handlers
			this.webSocketClient.ConnectionChanged += new WSDelegates.ConnectionChangedEventHandler(webSocketClient_ConnectionChanged);
			this.webSocketClient.TextMessageReceived += new WSDelegates.TextMessageReceivedEventHandler(webSocketClient_TextMessageReceived);
			this.webSocketClient.Error += new WSDelegates.ErrorEventHandler(webSocketClient_Error);
        }

		protected override void OnFormClosing(FormClosingEventArgs e)
		{
			if (this.webSocketClient.State != WebSocketState.Initialized && this.webSocketClient.State != WebSocketState.Disconnected)
			{
				e.Cancel = true;
				this.UseWaitCursor = true;
				this.webSocketClient.Disconnect();
				timer = new Timer();
				timer.Interval = 2000;
				timer.Tick += new EventHandler(timer_Tick);
				timer.Start();
			}

			base.OnFormClosing(e);
		}

		protected override void OnFormClosed(FormClosedEventArgs e)
		{
			if (this.timer != null)
			{
				this.timer.Dispose();
			}
			this.timer = null;

			if (this.webSocketClient != null)
			{
				this.webSocketClient.Dispose();
			}
			this.webSocketClient = null;

			if (this.fileLogger != null)
			{
				this.fileLogger.Close();
				this.fileLogger.Dispose();
			}
			this.fileLogger = null;
			
			base.OnFormClosed(e);
		}

		private bool InitializeNetwork(bool useDHCP)
		{
			return true;
		}

		private void timer_Tick(object sender, EventArgs e)
		{
			if (this.webSocketClient.State == WebSocketState.Initialized || this.webSocketClient.State == WebSocketState.Disconnected)
			{
				this.timer.Stop();
				this.Close();
			}
		}

		private void UpdateUI(int? webSocketState, string message)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(delegate { this.UpdateUI(webSocketState, message); }));
			}
			else
			{
				if (webSocketState != null)
				{
					WebSocketState ws = (WebSocketState)webSocketState;
					this.statusBarMessage.Text = ws.Name();
					if (ws == WebSocketState.Connected)
					{
						this.connectButton.Enabled = false;
						this.disconnectButton.Enabled = true;
						this.sendMsgButton.Enabled = true;
					}
					else if (ws == WebSocketState.Disconnected)
					{
						this.connectButton.Enabled = true;
						this.disconnectButton.Enabled = false;
						this.sendMsgButton.Enabled = false;
					}
				}

				if (message != null)
				{
					this.statusBarMessage.Text = message;
				}
			}
		}

		#endregion


		#region Event Handlers

		private void connectButton_Click(object sender, EventArgs e)
		{
			this.webSocketClient.Connect(serverUrl);
		}

		private void disconnectButton_Click(object sender, EventArgs e)
		{
			this.webSocketClient.Disconnect();
		}

		private void sendMsgButton_Click(object sender, EventArgs e)
		{
			Logger.WriteInfo("Main", "Message Sent: " + this.messageTextBox.Text);
			this.webSocketClient.SendText(this.messageTextBox.Text);
		}

		private void webSocketClient_ConnectionChanged(WebSocketState websocketState)
		{
			Logger.WriteInfo("Main", "Connection Status: " + websocketState.Name());
			this.UpdateUI((int)websocketState,  null);
		}

		private void webSocketClient_TextMessageReceived(string textMessage)
		{
			Logger.WriteInfo("Main", "Message Rcvd: " + textMessage);
		}

		private void webSocketClient_Error(string message, string stackTrace = null)
		{
			Logger.WriteError("Main", message, stackTrace);
			this.UpdateUI(null, "Error: " + message);
		}

		#endregion


		#region ILogger implementation

		private delegate void WriteLineCallback(string msgType, string date, string time, string source, string message, string stackTrace);

		private void WriteToLog(string msgType, string date, string time, string source, string message, string stackTrace)
		{
			// check if being called from a different thread
			if (this.logListView.InvokeRequired)
			{
				this.Invoke(new WriteLineCallback(WriteToLog), new object[] { msgType, date, time, source, message, stackTrace });
			}
			else
			{
				ListViewItem item = new ListViewItem();
				item.ForeColor = (msgType.Equals("Error") ? System.Drawing.Color.Red : System.Drawing.Color.Black);
				item.Text = msgType;
				item.SubItems.Add(date);
				item.SubItems.Add(time);
				item.SubItems.Add(source);
				item.SubItems.Add(message);
				item.SubItems.Add(stackTrace);
				this.logListView.Items.Add(item);
			}
		}

		public void WriteError(string source, string message, string stackTrace = "")
		{
			this.WriteToLog("Error", DateTime.Now.ToString("MM/dd/yyyy"), DateTime.Now.ToString("HH:mm:ss.fff"), source, message, stackTrace);
		}

		public void WriteInfo(string source, string message)
		{
			this.WriteToLog("Info", DateTime.Now.ToString("MM/dd/yyyy"), DateTime.Now.ToString("HH:mm:ss.fff"), source, message, "");
		}

		public void WriteDebug(string source, string message)
		{
			this.WriteToLog("Debug", DateTime.Now.ToString("MM/dd/yyyy"), DateTime.Now.ToString("HH:mm:ss.fff"), source, message, "");
		}

		#endregion
	}
}
